﻿#include"CentralCache.h"
#include"PageCache.h"

//调用main函数前就会创建该单例对象
CentralCache CentralCache::_sInstance;

// 获取⼀个⾮空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	//去list中查看是否还有未分配的span
	Span* it = list.Begin();

	while (it != list.End())
	{
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}

	//解开Central Cache桶锁，避免其他线程释放的内存还给桶时时阻塞
	list._mtx.unlock();

	//list中没有未分配的Span，只能去找pageCache要
	//访问pageCache需要加锁
	PageCache::getInstance()->getMutex().lock();
	Span* span = PageCache::getInstance()->newSpan(SizeClass::NumMovePage(size));
	span->_isUse = true;
	span->_objSize = size; 
	PageCache::getInstance()->getMutex().unlock();

	//对于获取到的Span进行切分不需要加锁，因为别的线程访问不到这个Span

	//计算大块内存的起始地址和大块内存的大小（size）
	char* start = (char*)(span->_pageId << PAGE_SHIFT); // 通过页号 * 8 KB获取大块内存的起始地址
	size_t bytes = span->_pageSize << PAGE_SHIFT; // 计算偏移量（字节数）
	char* end = start + bytes; //根据 起始地址  +  偏移量 计算出结束地址

	//把大块内存切成小块内存用自由链表链接起来
	//先切一块作为头节点方便尾插

	span->_freeList = start;
	start += size;
	void* tail = span->_freeList;
	int i = 1; // for test
	while (start < end)
	{
		//尾插
		++i;
		nextObj(tail) = start;
		tail = nextObj(tail); // tail = start;
		start += size;
	}

	//置空
	nextObj(tail) = nullptr;

	//将切好的Span挂回到桶里时，在加锁 
	list._mtx.lock();
	list.PushFront(span); // 将span头插到CentralCache桶里

	return span;
}

// 从中⼼缓存获取⼀定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = SizeClass::Index(size); // 获取的桶位置

	//中心缓存属于临界区资源需要锁保护
	_spanLists[index]._mtx.lock();

	//获取一个span
	Span* span = GetOneSpan(_spanLists[index], size);
	//span判断有效性
	assert(span);
	assert(span->_freeList);

	//从span中拿走batchNum个对象
	//不够的话能拿多少就拿多少
	start = span->_freeList;
	end = start;
	size_t i = 0;
	size_t actualNum = 1; // 统计拿走的个数

	while (i < batchNum - 1 && nextObj(end) != nullptr)
	{
		end = nextObj(end);
		i++;
		++actualNum;
}
	span->_freeList = nextObj(end);
	nextObj(end) = nullptr;
	span->_useCount += actualNum;

	//// 条件断点
/*	int j = 0;
	void* cur = start;
	while (cur)
	{
		cur = nextObj(cur);
		++j;
	}

	if (j != actualNum)
	{
		int x = 0;
	}*/

		
	//解锁
	_spanLists[index]._mtx.unlock();

	return actualNum;
}

// 将⼀定数量的对象释放到span跨度
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();

	while (start)
	{
		void* next = nextObj(start); // 保存下一个节点的地址

		// 通过建立的映射关系找到对应的span,将节点头插回span
		Span* span = PageCache::getInstance()->MapObjectToSpan(start); 
		nextObj(start) = span->_freeList; 
		span->_freeList = start;
		span->_useCount--; // 内存还回来就 - - usecount

		if (span->_useCount == 0) //切分出去的小块内存都被还回快来了
		{
			//这个span就可以再回收给page cache，pagecache可以再尝试去做前后页的合并

			//移除对应的span，并将链接关系修改
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;

			//将span还给PageCache，需要使用锁
			//先解掉桶锁，还完span后再上桶锁
			_spanLists[index]._mtx.unlock();

			//加锁，将span还给PageCache
			PageCache::getInstance()->getMutex().lock();
			PageCache::getInstance()->ReleaseSpanToPageCache(span);
			PageCache::getInstance()->getMutex().unlock();

			_spanLists[index]._mtx.lock();//加锁

		}
		start = next;
	}

	_spanLists[index]._mtx.unlock();

}